import { defineReactive, hasOwn, proxy, getTypeIndex, hyphenate, isObject, isPlainObject } from './helper.mjs'

export default function Vue(options) {
  this._init(options);
}

Vue.prototype._init = function (options) {
  const vm = this;
  vm.$options = {
    propsData: options.propsData,
    props: options.props,
  };
  initState(vm);
};

function initState(vm) {
  const opts = vm.$options;
  if (opts.props) {
    initProps(vm, opts.props);
  }
}

function initProps(vm, propsOptions) {
  const propsData = vm.$options.propsData;
  const props = (vm._props = {});
  for (const key in propsOptions) {
    const value = validateProp(key, propsOptions, propsData, vm);
    if (process.env.NODE_ENV !== 'production') {
      const hyphenatedKey = hyphenate(key);
      if (['key', 'ref', 'slot', 'slot-scope', 'is'].includes(hyphenatedKey)) {
        console.warn(
          `${hyphenatedKey}是保留的属性，不能被用作组件的prop`
        );
      }
      defineReactive(props, key, value, () => {
        console.warn(`避免直接修改prop值：${key}`);
      })
    } else {
      // 在生产环境中，不做上述的错误提示
      defineReactive(props, key, value);
    }
    if (!(key in vm)) {
      // 在vm上取key的值时，从vm._props上取
      proxy(vm, `_props`, key);
    }
  }
}

function validateProp(key, propsOptions, propsData, vm) {
  const prop = propsOptions[key];
  const absent = !hasOwn(propsData, key);
  let value = propsData[key];
  const booleanIndex = getTypeIndex(Boolean, prop.type);
  if (booleanIndex > -1) {
    if (absent && !hasOwn(prop, "default")) {
      value = false;
    } else if (value === "" || value === hyphenate(key)) {
      const stringIndex = getTypeIndex(String, prop.type);
      if (stringIndex < 0 || booleanIndex < stringIndex) {
        value = true;
      }
    }
  }
  if (value === undefined) {
    value = getPropDefaultValue(vm, prop, key);
    // 对value进行监听
    // observe(value);
  }
  if (process.env.NODE_ENV !== 'production') {
    assertProp(prop, key, value, vm, absent)
  }
  return value;
}

function getPropDefaultValue(vm, prop, key) {
  if (!hasOwn(prop, 'default')) {
    return undefined;
  }
  const def = prop.default;
  if (process.env.NODE_ENV !== 'production' && isObject(def)) {
    console.log('类型Object或Array的默认值应当用一个函数来返回')
  }
  if (vm && vm.$options.propsData && vm.$options.propsData[key] === undefined && vm._props[key] !== undefined) {
    return vm._props[key]
  }
  return typeof def === 'function' && getType(prop.type) !== 'Function' ? def.call(vm) : def;
}

function assertProp(prop, key, value, vm, absent) {
  // 定义了required但是没有传
  if (prop.required && absent) {
    console.log('定义了required但是没有传值')
  }
  // 值为undefined或null，且未定义required，则无需再校验
  if (value == null && !prop.required) {
    return
  }
  // 校验type设定
  let type = prop.type;
  let valid = !type || type === true;
  const expectedTypes = [];
  if (type) {
    if (!Array.isArray(type)) {
      type = [type];
    }
    for (let i = 0; i < type.length && !valid; i++) {
      const assertedType = assertType(value, type[i], vm);
      expectedTypes.push(assertedType.expectedType || '')
      valid = assertedType.valid
    }
  }
  const hasExpectedTypes = expectedTypes.some(t => t)
  if (!valid && hasExpectedTypes) {
    console.log(key + '的值类型不对');
    return;
  }
  let validator = prop.validator;
  if (validator) {
    if (!validator(value)) {
      console.log(key + '的值未通过validator校验')
    }
  }
}

const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol|BigInt)$/;
function assertType(value, type, vm) {
  let valid;
  const expectedType = getType(type);
  if (simpleCheckRE.test(expectedType)) {
    const t = typeof value;
    valid = t === expectedType.toLowerCase();
    if (!valid && t === 'object') {
      valid = value instanceof type;
    }
  } else if (expectedType === 'Object') {
    valid = isPlainObject(value)
  } else if (expectedType === 'Array') {
    valid = Array.isArray(value);
  } else {
    try {
      valid = value instanceof type
    } catch (error) {
      console.warn(type + "不是一个构造函数");
    }
  }

  return {
    valid,
    expectedType
  }
}
